import java.io.*;
import java.util.*;

public class CustomerQueue extends Observable {

	// ArrayList of customer objects in queue, needs indexed order but not string keys
	private ArrayList<Customer> customerList;

	/**
	 * Constructor: create an empty ArrayList to store customers
	 */
	public CustomerQueue() {
		customerList = new ArrayList<Customer> ();
	}

	/**
	 * Add Customer object to the queue
	 * @param s Customer object to add
	 */
	private void add(Customer s) {
		customerList.add(s);

		Logger.INSTANCE.appendLog("main", "Customer '"+s.getName().getFullName()+"' added to the queue\n");
		
		// Tell observers the queue has changed
		try {
			this.setChanged();
			this.notifyObservers();
		} catch (Exception e) {
			e.printStackTrace();
		}		
	}

	/**
	 * Add new Customer to the queue at specified position from basic input data
	 * @param position int specific position to add customer at
	 * @param nameString String name(s) of customer
	 * @param parcelId String ID of parcel customer is looking for
	 */
	private void addCustomerAtPosition(int position,String nameString,String parcelId) {
		// Customer Name - String
		// Processes string which can contain up to three names (first, middle, last)
		// separated by spaces, and creates a Name object 
		Name name = new Name(nameString);
		//create customer object and add to the list
		Customer s = new Customer(position,name,parcelId);
		this.add(s);
	}

	/**
	 * Add new Customer to the end of the queue
	 * @param nameString String name(s) of customer
	 * @param parcelId String ID of parcel customer is looking for
	 */
	public void addCustomer(String nameString,String parcelId) {
		int lastPosition = getLastPosition();
		addCustomerAtPosition(lastPosition+1,nameString,parcelId);
	}

	/**
	 * Get last position in queue
	 * @return int position number of back of queue
	 */
	public int getLastPosition() {
		int lastPosition = 1;
		for (Customer s  : customerList){
			if(s.getPosition() > lastPosition) {
				lastPosition = s.getPosition();
			}
		}
		return lastPosition;
	}

	/**
	 * Returns a list of all customers in the queue
	 * including everything we know about each, one per line
	 * @return String representation of customer queue
	 */
	public String outputQueue()
	{
		// Print "table" header to show columns
		String report = String.format("%-5s", "Pos.");
		report += String.format("%-15s", "Full Name");
		report += String.format("%-7s", "Parcel") + "\n";

		for (Customer s  : customerList){
			report += String.format("%-5s", s.getPosition());
			report += String.format("%-15s", s.getName().getFullName() );
			report += String.format("%-7s", s.getParcelId());
			report += "\n";
		}
		return report;
	}

	/**
	 * Returns number of Customers in the queue
	 * @return int size of customer list
	 */
	public int getSize() {
		return customerList.size();
	}

	/**
	 * Returns the Customer object at specified ArrayList index
	 * @param index int containing the index of the customer to get
	 * @return Customer object or null if incorrect index
	 */
	public Customer getAtIndex(int index) {
		return customerList.get(index);
	}

	/**
	 * Returns the Customer object with a specified Parcel ID
	 * @param parcelId String containing the parcelId (numerical or otherwise) of the customer to find
	 * @return Customer object found, or null if none found
	 */
	public Customer findByParcelId(String parcelId)
	{
		for (Customer s : customerList)
		{
			if (s.getParcelId().equals(parcelId))
			{
				return s;
			}
		}
		return null;
	}

	/**
	 * Returns the Customer object at a specified queue position
	 * @param position int containing the position of the customer to get
	 * @return Customer object found, or null if queue position doesn't exist
	 */
	public Customer getAtPosition(int position)
	{
		if(getSize()==0) return null;
		for (Customer s : customerList)
		{
			if (s.getPosition() == position)
			{
				return s;
			}
		}
		return null;
	}

	/**
	 * Returns the Customer object at the front of the queue
	 * @return Customer object found, or null if queue is empty
	 */
	public Customer getNextCustomer()
	{
		return getAtPosition(1);
	}

	/**
	 * Returns the Customer object at the front of the queue
	 * @return Customer object found, or null if queue is empty
	 */
	public Customer getRemoveNextCustomer()
	{
		Customer next = getAtPosition(1);
		if(next==null) return null;
		removeCustomer();
		return next;
	}

	/**
	 * Removes the Customer at the front of the queue
	 * Fairly inefficient algorithm; loops through all customers and adds them to new ArrayList if they are not at the front
	 * Decrements the position of each before adding to new ArrayList, then replaces customerList with re-indexed version
	 */
	public void removeCustomer()
	{
		Logger.INSTANCE.appendLog("main", "Customer '"+getAtPosition(1).getName().getFullName()+"' removed from the queue\n");
		
		ArrayList<Customer> customerListReindexed = new ArrayList<Customer> ();

		for (Customer customer : customerList) {
			int oldPosition = customer.getPosition();
			if (oldPosition > 1) {
				customer.setPosition(oldPosition - 1);
				customerListReindexed.add(customer);
			}
		}
		
		customerList = customerListReindexed;
		// Tell observers the queue has changed
		try {
			this.setChanged();
			this.notifyObservers();
		} catch (Exception e) {
			e.printStackTrace();
		}		
	}

	/** reads file with given name, extracting customer data
	 * Blank lines are skipped
	 * @param filename the name of the input file
	 */
	public void readFile(String filename) throws FileNotFoundException {
		InputStream is = getClass().getResourceAsStream(filename);
		Scanner scanner = new Scanner(is);
		while (scanner.hasNextLine()) {
			//read first line and process it
			String inputLine = scanner.nextLine();
			if (inputLine.length() != 0) {//ignored if blank line
				//create customer object and add to the list
				Customer s = new Customer(inputLine);
				this.add(s);
			}
		}
		scanner.close();

		// Tell observers the queue has changed since we just added a batch of customers
		try {
			this.setChanged();
			this.notifyObservers();
		} catch (Exception e) {
			e.printStackTrace();
		}		
	}

}